package com.lb.api;

import com.alibaba.fastjson.JSON;
import com.lb.base.util.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Type;

import static com.alibaba.fastjson.serializer.SerializerFeature.WriteNullStringAsEmpty;
import static com.lb.base.constants.BaseConstants.*;
import static org.springframework.http.HttpMethod.POST;

/**
 * 请求日志拦截器
 *
 * @author xiemingzhi
 * @since 1.0
 */
@Slf4j
@ControllerAdvice
public class ApiLogInterceptorAdvice implements HandlerInterceptor, RequestBodyAdvice, ResponseBodyAdvice<Object>, Ordered {

    @Override
    public int getOrder() {
        return -5;
    }

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
        String method = request.getMethod();
        ApiLogRequest apiLogRequest = ApiLogRequest.build(request);

        Long currentTimeMillis = System.currentTimeMillis();
        request.setAttribute(REQUEST_ATTRIBUTE_KEY_API_CURRENT_TIME_MILLIS, currentTimeMillis);

        String userId = RequestUtils.getUserId();
        apiLogRequest.setUserId(userId);

        if (POST.equals(HttpMethod.resolve(method))) {
            request.setAttribute(REQUEST_ATTRIBUTE_KEY_API_LOG_REQUEST, apiLogRequest);
        } else {
            log.info(LOG_FORMAT_KEY_API_LOG_REQUEST, JSON.toJSONString(apiLogRequest, WriteNullStringAsEmpty));
        }
        return true;
    }

    @Override
    public void postHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, ModelAndView modelAndView) {

    }

    @Override
    public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) {

    }


    @Override
    public boolean supports(@NonNull MethodParameter methodParameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @NonNull
    @Override
    public HttpInputMessage beforeBodyRead(@NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        return inputMessage;
    }

    @NonNull
    @Override
    public Object afterBodyRead(@NonNull Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        ApiLogRequest apiLogRequest = RequestUtils.getApiLogRequest();
        if (apiLogRequest != null) {
            apiLogRequest.setBody(body);
            log.info(LOG_FORMAT_KEY_API_LOG_REQUEST, JSON.toJSONString(apiLogRequest, WriteNullStringAsEmpty));
        }
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, @NonNull HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        ApiLogRequest apiLogRequest = RequestUtils.getApiLogRequest();
        if (apiLogRequest != null) {
            log.info(LOG_FORMAT_KEY_API_LOG_REQUEST, JSON.toJSONString(apiLogRequest, WriteNullStringAsEmpty));
        }
        return body;
    }


    @Override
    public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, @NonNull MethodParameter returnType, @NonNull MediaType selectedContentType, @NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response) {
        String userId = RequestUtils.getUserId();
        ApiLogResponse apiLogResponse = new ApiLogResponse();
        apiLogResponse.setBody(body);
        apiLogResponse.setUserId(userId);
        apiLogResponse.setTime(System.currentTimeMillis()- RequestUtils.getCurrentTimeMillis());
        log.info(LOG_FORMAT_KEY_API_LOG_RESPONSE, JSON.toJSONString(apiLogResponse, WriteNullStringAsEmpty));
        return body;
    }

}
